/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package IOFPGA;

import app.Com;
import app.Parameters;
import compiladorEntidad.Entidad;
import core.SerialPort;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import interfaz.GUIPrincipal;


/**
 *
 * @author Felipe Serrano
 */
public class EntradaSalida {
    /**
     * Parametros de configuración que se introducen al puerto serie
     */
    private Parameters param;
    /**
     * Interfaz para el manejo del puerto RS232
     */
    private Com com;
    
    /**
     * Variable para saber cuantas palabras de 32 bits es una 
     * entrada del testbench
     */
    private int palabrasPorEnvio;
    

    /**
     * Puerto COM al que está conectada la FPGA
     */
    private String numPuerto;

    private SerialPort puerto;
    
    
    /*
     * Necesita saber el número del puerto serie. En la forma "COM1", "COM2" etc.
     * El tipo de adaptador que se usará (32, 64, 128 etc).
     * El número de bits de salida que tiene el circuito.
     */
    public EntradaSalida(String puerto, int tipoAdaptador) {
        param = null;
        com = null;
        numPuerto = puerto;
        palabrasPorEnvio = tipoAdaptador/32;
    }

    public Com getCom() {
        return com;
    }
    
    public void setComNull(){
        com = null;
    }
    
    /**
     * Función que inicializa el puerto serie necesario para la comuncicación
     * con la FPGA. Comprueba que esté libre el puerto y que la maquina sobre
     * la que estamos ejecutando tenga el puerto COM1.
     * @return Cierto si todo ha sido correcto, falso si ha habido algún error.
     */
    public boolean inicializarPuertoSerie() {
        boolean correcto = true;
        try {
            param = new Parameters();
            param.setPort(numPuerto);
            param.setBaudRate("19200");
            puerto = new SerialPort();
            if (puerto.getStateSerialPortC(numPuerto).equals("free")) {
                com = new Com(param);
            } else {
                correcto = false;
            }
        } catch (Exception ex) {
            System.out.println(ex);
            System.exit(0);
        }
        return correcto;
    }

    public void cerrarPuertoSerie(){
        try {
            com.close();
        } catch (Exception ex) {
            Logger.getLogger(EntradaSalida.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public boolean esperarPalabraSyn() {
        boolean encontrado = false;
        boolean fin = false;
        String dato = "";
        int espera_max = 0;
        try {
            while (!fin) {
                dato = recibirBinaria(8);
                espera_max++;
                if (dato.equals("11001100")) {
                    encontrado = true;
                    fin = true;
                } else if (espera_max == 10) {
                    encontrado = false;
                    fin = true;
                }
            }
        } catch (Exception ex) {
            System.out.println("No se recibió la palabra de sincronización");
            dato = "11111111";
        }
        return encontrado;
    }


    public boolean comprobarComunicacion(){
        try {
            //Comando de sincronización
            enviarBinaria(6);
            String respuesta = recibirBinaria(32);
            if(!respuesta.equals("11110000111100001111000011110000")){
                return false;
            }
        } catch (Exception ex) {
            return false;
        }
        return true;
    }


    /*
     * Envia una entrada del testbench. Al ser de tamaño del circuito variable 
     * lo descompone palabras de 32 bits
     */
    public void enviarEntradaTB(String s){
        s = extenderString(s);
        for(int i=0;i<palabrasPorEnvio;i++){
            enviarBinaria(s.substring(i*32, i*32+32));
        }
    }
    
    
     /*
     * Recibe una entrada del testbench. Al ser de tamaño del circuito variable 
     * lo descompone palabras de 32 bits
     */
    public String recibirEntradaTB(int tam){
        String s="";
        String palabra;
        for(int i=0;i<palabrasPorEnvio;i++){
            palabra = recibirBinaria(32);
            if(palabra == null) return null;
            s = s + palabra;
        }
        return s.substring(0,tam);
    }
    
    
    /**
     * Envía una cadena binaria de 32 bits a la FPGA en 4 partes, porque la palabra es de 32 bits
     * y solo podemos enviar 8 por cada ciclo
     * @param Cadena de 32 de bits que deseamos enviar.
     */
    public boolean enviarBinaria(String s) {
        int cad;
        int dif = 0;
        if (s.length() < 32) {
            dif = 32 - s.length();
        }
        for (int i = 0; i < dif; i++) {
            s = "0" + s; //se añaden 0's por la izquierda
        }
        try {
            for (int i = 0; i < 4; i++) {
                cad = traduceString(s.substring(i * 8, i * 8 + 8));
                if (cad == -1) {
                    return false;
                }
                com.sendSingleData(cad);
                
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
        return true;
    }
    
    /**
     * Versión de enviarBinaria para números enteros
     * @param x número a enviar
     * @return Falso si no se ha podido enviar
     */
    public boolean enviarBinaria(int x){
        return enviarBinaria(Integer.toBinaryString(x));
    }
    
    
     /**
     * Procedimiento que lee de la FPGA un entero y lo transforma a la cadena de bits.
     * @return Cadena de bits recibida.
     */
    public String recibirBinaria(int numBitsSalida) {
        int num;
        String cadenaRecibida = "";
        try {
            for (int i = 0; i < numBitsSalida / 8; i++) {
                num = com.receiveSingleDataInt();
                cadenaRecibida = cadenaRecibida + this.convertirByteBinario(num);
            }
        } catch (Exception ex) {
            return null;
        }
        return cadenaRecibida;
    }
    
    /**
     * Versión de recibirBinaria en formato entero
     * @return Entero recibido
     */
    public int recibirEntero() {
        String str;
        str = recibirBinaria(32);
        return traduceString(str);
    }
    
    
    /**
     * Convierte a entero una una cadena en formato binario
     * @param s cadena a traducir.
     * @return Entero equivalente al String introducido o -1 si el formato no es
     * correcto (no son 0's y 1's)
     */
    private int traduceString(String s) {
        int n = 0;
        int peso = 1;
        for (int i = s.length() - 1; i >= 0; i--) {
            if (s.charAt(i) != '0' && s.charAt(i) != '1') {
                return -1;
            }
            if (s.charAt(i) == '1') {
                n = n + peso;
            }
            peso = peso * 2;
        }
        return n;
    }
    
    private String extenderString(String s){
        int i;
        for(i=s.length(); i<palabrasPorEnvio*32; i++){
            s=s+"0";
        }
        return s;
    }
    
    public String extenderStringReverso(String s){
        int i;
        for(i=s.length(); i<palabrasPorEnvio*32; i++){
            s="0"+s;
        }
        return s;
    }


    /**
     * Procedimiento que transforma un byte (representado mediante un entero)
     * a una cadena de ceros y  unos. Utiliza el algoritmo de la división por 2.
     * @param recibido Byte a traducir.
     * @return Cadena equivalente en bits al byte recibido.
     */
    private String convertirByteBinario(int recibido) {
        String salida = "";
        int numero;
        numero = recibido;
        int long_byte = 8;
        for (int i = 0; i < long_byte; i++) {
            if (numero % 2 == 0) {
                salida = "0" + salida;
            } else {
                salida = "1" + salida;
            }
            numero = numero / 2;
        }
        return salida;
    }
    

}
